"use strict";
exports.__esModule = true;
exports.formatComponentName = exports.parseStackFrames = exports.parseStackLine = exports.getErrorUid = exports.getErrorKey = exports.mechanismType = void 0;
var userVitals_1 = require("./userVitals");
// TODO React 异常捕获
// TODO 类型抽取
// 错误类型
var mechanismType;
(function (mechanismType) {
    mechanismType["JS"] = "js";
    mechanismType["RS"] = "resource";
    mechanismType["UJ"] = "unhandledrejection";
    mechanismType["HP"] = "http";
    mechanismType["CS"] = "cors";
    mechanismType["VUE"] = "vue";
})(mechanismType = exports.mechanismType || (exports.mechanismType = {}));
// 判断是 JS异常、静态资源异常、还是跨域异常
var getErrorKey = function (event) {
    var isJsError = event instanceof ErrorEvent;
    if (!isJsError)
        return mechanismType.RS;
    return event.message === 'Script error.'
        ? mechanismType.CS
        : mechanismType.JS;
};
exports.getErrorKey = getErrorKey;
// 对每一个错误详情，生成一串编码
var getErrorUid = function (input) {
    return window.btoa(unescape(encodeURIComponent(input)));
};
exports.getErrorUid = getErrorUid;
// 正则表达式，用以解析堆栈split后得到的字符串
var FULL_MATCH = /^\s*at (?:(.*?) ?\()?((?:file|https?|blob|chrome-extension|address|native|eval|webpack|<anonymous>|[-a-z]+:|.*bundle|\/).*?)(?::(\d+))?(?::(\d+))?\)?\s*$/i;
// 限制只追溯10个
var STACKTRACE_LIMIT = 10;
// 解析每一行
function parseStackLine(line) {
    var lineMatch = line.match(FULL_MATCH);
    if (!lineMatch)
        return {};
    var filename = lineMatch[2];
    var functionName = lineMatch[1] || '';
    var lineno = parseInt(lineMatch[3], 10) || undefined;
    var colno = parseInt(lineMatch[4], 10) || undefined;
    return {
        filename: filename,
        functionName: functionName,
        lineno: lineno,
        colno: colno
    };
}
exports.parseStackLine = parseStackLine;
// 解析错误堆栈
function parseStackFrames(error) {
    var stack = error.stack;
    // 无 stack 时直接返回
    if (!stack)
        return [];
    var frames = [];
    for (var _i = 0, _a = stack.split('\n').slice(1); _i < _a.length; _i++) {
        var line = _a[_i];
        var frame = parseStackLine(line);
        if (frame) {
            frames.push(frame);
        }
    }
    return frames.slice(0, STACKTRACE_LIMIT);
}
exports.parseStackFrames = parseStackFrames;
// 获取报错组件名
var classifyRE = /(?:^|[-_])(\w)/g;
var classify = function (str) {
    return str.replace(classifyRE, function (c) { return c.toUpperCase(); }).replace(/[-_]/g, '');
};
var ROOT_COMPONENT_NAME = '<Root>';
var ANONYMOUS_COMPONENT_NAME = '<Anonymous>';
var formatComponentName = function (vm, includeFile) {
    if (!vm) {
        return ANONYMOUS_COMPONENT_NAME;
    }
    if (vm.$root === vm) {
        return ROOT_COMPONENT_NAME;
    }
    var options = vm.$options;
    var name = options.name || options._componentTag;
    var file = options.__file;
    if (!name && file) {
        var match = file.match(/([^/\\]+)\.vue$/);
        if (match) {
            name = match[1];
        }
    }
    return ((name ? "<".concat(classify(name), ">") : ANONYMOUS_COMPONENT_NAME) +
        (file && includeFile !== false ? " at ".concat(file) : ''));
};
exports.formatComponentName = formatComponentName;
// 初始化的类
var default_1 = /** @class */ (function () {
    function default_1(engineInstance, options) {
        var _this = this;
        this.errorSendHandler = function (data) {
            console.log("js异常", data);
        };
        // 初始化 JS异常 的数据获取和上报
        this.initJsError = function () {
            var handler = function (event) {
                // 阻止向上抛出控制台报错
                event.preventDefault();
                // 如果不是 JS异常 就结束
                if ((0, exports.getErrorKey)(event) !== mechanismType.JS)
                    return;
                var exception = {
                    // 上报错误归类
                    mechanism: {
                        type: mechanismType.JS
                    },
                    // 错误信息
                    value: event.message,
                    // 错误类型
                    type: (event.error && event.error.name) || 'UnKnowun',
                    // 解析后的错误堆栈
                    stackTrace: {
                        frames: parseStackFrames(event.error)
                    },
                    // 用户行为追踪 breadcrumbs 在 errorSendHandler 中统一封装
                    // 页面基本信息 pageInformation 也在 errorSendHandler 中统一封装
                    // 错误的标识码
                    errorUid: (0, exports.getErrorUid)("".concat(mechanismType.JS, "-").concat(event.message, "-").concat(event.filename)),
                    // 附带信息
                    meta: {
                        // file 错误所处的文件地址
                        file: event.filename,
                        // col 错误列号
                        col: event.colno,
                        // row 错误行号
                        row: event.lineno
                    }
                };
                // 一般错误异常立刻上报，不用缓存在本地
                _this.errorSendHandler(exception);
            };
            window.addEventListener('error', function (event) { return handler(event); }, true);
        };
        // 初始化 静态资源异常 的数据获取和上报
        this.initResourceError = function () {
            var handler = function (event) {
                event.preventDefault(); // 阻止向上抛出控制台报错
                // 如果不是跨域脚本异常,就结束
                if ((0, exports.getErrorKey)(event) !== mechanismType.RS)
                    return;
                var target = event.target;
                var exception = {
                    // 上报错误归类
                    mechanism: {
                        type: mechanismType.RS
                    },
                    // 错误信息
                    value: '',
                    // 错误类型
                    type: 'ResourceError',
                    // 用户行为追踪 breadcrumbs 在 errorSendHandler 中统一封装
                    // 页面基本信息 pageInformation 也在 errorSendHandler 中统一封装
                    // 错误的标识码
                    errorUid: (0, exports.getErrorUid)("".concat(mechanismType.RS, "-").concat(target.src, "-").concat(target.tagName)),
                    // 附带信息
                    meta: {
                        url: target.src,
                        html: target.outerHTML,
                        type: target.tagName
                    }
                };
                // 一般错误异常立刻上报，不用缓存在本地
                _this.errorSendHandler(exception);
            };
            window.addEventListener('error', function (event) { return handler(event); }, true);
        };
        // 初始化 Promise异常 的数据获取和上报
        this.initPromiseError = function () {
            var handler = function (event) {
                event.preventDefault(); // 阻止向上抛出控制台报错
                var value = event.reason.message || event.reason;
                var type = event.reason.name || 'UnKnowun';
                var exception = {
                    // 上报错误归类
                    mechanism: {
                        type: mechanismType.UJ
                    },
                    // 错误信息
                    value: value,
                    // 错误类型
                    type: type,
                    // 解析后的错误堆栈
                    stackTrace: {
                        frames: parseStackFrames(event.reason)
                    },
                    // 用户行为追踪 breadcrumbs 在 errorSendHandler 中统一封装
                    // 页面基本信息 pageInformation 也在 errorSendHandler 中统一封装
                    // 错误的标识码
                    errorUid: (0, exports.getErrorUid)("".concat(mechanismType.UJ, "-").concat(value, "-").concat(type)),
                    // 附带信息
                    meta: {}
                };
                // 一般错误异常立刻上报，不用缓存在本地
                _this.errorSendHandler(exception);
            };
            window.addEventListener('unhandledrejection', function (event) { return handler(event); }, true);
        };
        // 初始化 HTTP请求异常 的数据获取和上报
        this.initHttpError = function () {
            var loadHandler = function (metrics) {
                // 如果 status 状态码小于 400,说明没有 HTTP 请求错误
                if (metrics.status < 400)
                    return;
                var value = metrics.response;
                var exception = {
                    // 上报错误归类
                    mechanism: {
                        type: mechanismType.HP
                    },
                    // 错误信息
                    value: value,
                    // 错误类型
                    type: 'HttpError',
                    // 错误的标识码
                    errorUid: (0, exports.getErrorUid)("".concat(mechanismType.HP, "-").concat(value, "-").concat(metrics.statusText)),
                    // 附带信息
                    meta: {
                        metrics: metrics
                    }
                };
                // 一般错误异常立刻上报，不用缓存在本地
                _this.errorSendHandler(exception);
            };
            (0, userVitals_1.proxyXmlHttp)(null, loadHandler);
            (0, userVitals_1.proxyFetch)(null, loadHandler);
        };
        // 初始化 跨域异常 的数据获取和上报
        this.initCorsError = function () {
            var handler = function (event) {
                // 阻止向上抛出控制台报错
                event.preventDefault();
                // 如果不是跨域脚本异常,就结束
                if ((0, exports.getErrorKey)(event) !== mechanismType.CS)
                    return;
                var exception = {
                    // 上报错误归类
                    mechanism: {
                        type: mechanismType.CS
                    },
                    // 错误信息
                    value: event.message,
                    // 错误类型
                    type: 'CorsError',
                    // 错误的标识码
                    errorUid: (0, exports.getErrorUid)("".concat(mechanismType.JS, "-").concat(event.message)),
                    // 附带信息
                    meta: {}
                };
                // 自行上报异常，也可以跨域脚本的异常都不上报;
                _this.errorSendHandler(exception);
            };
            window.addEventListener('error', function (event) { return handler(event); }, true);
        };
        // 只需要在外部把初始化好的 Vue 对象传入即可~
        // 初始化 Vue异常 的数据获取和上报
        this.initVueError = function (app) {
            app.config.errorHandler = function (err, vm, info) {
                var componentName = (0, exports.formatComponentName)(vm, false);
                var exception = {
                    // 上报错误归类
                    mechanism: {
                        type: mechanismType.VUE
                    },
                    // 错误信息
                    value: err.message,
                    // 错误类型
                    type: err.name,
                    // 解析后的错误堆栈
                    stackTrace: {
                        frames: parseStackFrames(err)
                    },
                    // 错误的标识码
                    errorUid: (0, exports.getErrorUid)("".concat(mechanismType.JS, "-").concat(err.message, "-").concat(componentName, "-").concat(info)),
                    // 附带信息
                    meta: {
                        // 报错的Vue组件名
                        componentName: componentName,
                        // 报错的Vue阶段
                        hook: info
                    }
                };
                // 一般错误异常立刻上报，不用缓存在本地
                _this.errorSendHandler(exception);
            };
        };
        console.log(engineInstance);
        var Vue = options.Vue;
        this.engineInstance = engineInstance;
        this.submitErrorUids = [];
        // 初始化 js错误
        this.initJsError();
        // 初始化 静态资源加载错误
        this.initResourceError();
        // 初始化 Promise异常
        this.initPromiseError();
        // 初始化 HTTP请求异常
        this.initHttpError();
        // 初始化 跨域异常
        this.initCorsError();
        // 初始化 Vue异常
        // this.initVueError(Vue);
    }
    return default_1;
}());
exports["default"] = default_1;
